package rjp.solution;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import rjp.map.SantaMap;

/**
 *
 * @author mwh
 */
public class Solution implements Comparable<Solution> {

    private List<Integer> pathA;
    private List<Integer> pathB;

    private Float aLen=null;
    private Float bLen=null;
    private Float score=null;
    
    private SantaMap map;
    
    private Set<Long> usedConnections=null;
    
    public Solution(SantaMap map, List<Integer> pathA, List<Integer> pathB) {
        this.pathA = pathA;
        this.pathB = pathB;
        this.map=map;
    }
    
    public Solution(SantaMap map, String fn) throws IOException
    {
    	File file = new File("solutions/"+fn);  
    	FileReader fr = new FileReader(file);    
    	BufferedReader br = new BufferedReader(fr);                                                   
    	br.readLine();
    	ArrayList<Integer> pathA=new ArrayList<Integer>();
    	ArrayList<Integer> pathB=new ArrayList<Integer>();
    	String line;
    	while((line=br.readLine( )) != null)   
    	{  
    		String[] c=line.split(",");
    		pathA.add(Integer.parseInt(c[0]));
    		pathB.add(Integer.parseInt(c[1]));
    	}
    	br.close();
    	this.map=map;
    	this.pathA=pathA;
    	this.pathB=pathB;
    }
    
    public int getCityA(int index)
    {
    	return this.pathA.get(index);
    }
    
    public int getCityB(int index)
    {
    	return this.pathB.get(index);
    }
    
    public List<Integer> getSubListA(int fromIndex, int toIndex)
    {
    	return this.pathA.subList(fromIndex, toIndex);
    }
    
    public float getLenghtA()
    {
    	if (aLen==null)
    	{
    		aLen=map.getPathLength(this.pathA);
    	}
    	return aLen;
    }
    
    public float getLenghtB()
    {
    	if (bLen==null)
    	{
    		bLen=map.getPathLength(this.pathB);
    	}
    	return bLen;
    }
    
    public float getScore()
    {
    	return Math.max(this.getLenghtA(), this.getLenghtB());
    }
    
    public boolean isConnection(MapConnection connection)
    {
    	if (this.usedConnections==null)
    	{
    		this.isLegal();
    	}
    	long key=(long) connection.getA()+(long) map.size()*(long) connection.getB();
    	return this.usedConnections.contains(key);
    }
    
    public void writeToFile(String fn) throws IOException
    {
    	FileWriter outFile = new FileWriter("solutions/"+fn);
    	PrintWriter out = new PrintWriter(outFile);

    	out.println("path1,path2");
    	Iterator<Integer> it1=this.pathA.iterator();
    	Iterator<Integer> it2=this.pathB.iterator();
    	while(it1.hasNext())
    	{
    		out.println(it1.next().toString()+ ","+ it2.next().toString());
    	}
    	out.close();
    }
    
    
    public boolean isLegal()
    {
    	if(this.pathA.size()!=this.map.size() || this.pathB.size()!=this.map.size())
    	{
    		return false;
    	}
  
    	usedConnections=new HashSet<Long>();
    	Set<Integer> usedPoints=new HashSet<Integer>();
    	Iterator<Integer> i=this.pathA.iterator();
    	int prev=i.next();
    	usedPoints.add(prev);
    	while(i.hasNext())
    	{
    		int next=i.next();
    		MapConnection connection=new MapConnection(prev, next);
    		long key=(long) connection.getA()+(long) map.size()*(long) connection.getB();
    		if (usedConnections.contains(key))
    		{
    			return false;
    		} 

    		if(usedPoints.contains(next))
    		{
    			return false;
    		}
    		usedConnections.add(key);
    		usedPoints.add(next);
    		prev=next;
    	}
    	
    	i=this.pathB.iterator();
    	prev=i.next();
    	usedPoints=new HashSet<Integer>();
    	usedPoints.add(prev);
    	while(i.hasNext())
    	{
    		int next=i.next();
    		MapConnection connection=new MapConnection(prev, next);
    		long key=(long) connection.getA()+(long) map.size()*(long) connection.getB();
    		if (usedConnections.contains(key))
    		{
    			return false;
    		} 
    		
    		if(usedPoints.contains(next))
    		{
    			return false;
    		}
    		usedPoints.add(next);
    		usedConnections.add(key);
    		prev=next;
    	}
    	
    	return true;
    }

    @Override
    public int compareTo(Solution o) {
        return this.score.compareTo(o.score);
    }

	public void setPathA(ArrayList<Integer> path) {
		this.pathA=path;
		this.aLen=null;
	    this.bLen=null;
	    this.score=null;
	    this.usedConnections=null;
	}

	public void swapAB() {
		List<Integer> temp=this.pathA;
		this.pathA=this.pathB;
		this.pathB=temp;
		
		float ltemp=this.aLen;
		this.aLen=this.bLen;
		this.bLen=ltemp;
	}
    
    
	public String toString()
	{
		String s="Solution: \n";
		s+="Path A: ";
		for(int i=0; i<Math.min(100, this.pathA.size()); i++)
		{
			s+=this.pathA.get(i)+" ";
		}
		s+="\nLength A: "+this.getLenghtA();
		
		s+="\nPath B: ";
		for(int i=0; i<Math.min(100, this.pathB.size()); i++)
		{
			s+=this.pathB.get(i)+" ";
		}
		s+="\nLength B: "+this.getLenghtB();
		
		return s;
	}
}
